package com.jingdianjichi.practice.server.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.jingdianjichi.practice.api.enums.IsDeletedFlagEnum;
import com.jingdianjichi.practice.api.req.*;
import com.jingdianjichi.practice.api.resp.*;
import com.jingdianjichi.practice.server.entity.dto.SubjectDTO;
import com.jingdianjichi.practice.server.entity.dto.SubjectDetailDTO;
import com.jingdianjichi.practice.server.entity.dto.SubjectOptionDTO;
import com.jingdianjichi.practice.server.entity.dto.UserInfo;
import com.jingdianjichi.practice.server.entity.po.*;
import com.jingdianjichi.practice.api.enums.AnswerStatusEnum;
import com.jingdianjichi.practice.api.enums.CompleteStatusEnum;
import com.jingdianjichi.practice.api.enums.SubjectInfoTypeEnum;
import com.jingdianjichi.practice.server.mapper.*;
import com.jingdianjichi.practice.server.rpc.UserRpc;
import com.jingdianjichi.practice.server.service.PracticeDetailService;
import com.jingdianjichi.practice.server.util.DateUtils;
import com.jingdianjichi.practice.server.util.LoginUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class PracticeDetailServiceImpl implements PracticeDetailService {

    @Resource
    private PracticeDetailDao practiceDetailDao;

    @Resource
    private PracticeSetDao practiceSetDao;

    @Resource
    private PracticeSetDetailDao practiceSetDetailDao;

    @Resource
    private PracticeDao practiceDao;

    @Resource
    private SubjectDao subjectDao;

    @Resource
    private SubjectRadioDao subjectRadioDao;

    @Resource
    private SubjectMultipleDao subjectMultipleDao;

    @Resource
    private SubjectJudgeDao subjectJudgeDao;

    @Resource
    private SubjectMappingDao subjectMappingDao;

    @Resource
    private SubjectLabelDao subjectLabelDao;

    @Resource
    private UserRpc userRpc;

    //提交套卷
    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
    public Boolean submit(SubmitPracticeDetailReq req) {
        //套卷信息
        PracticePO practicePO = new PracticePO();

        //获得用户套卷表的id,每个题的信息我们已经存进去了
        Long practiceId = req.getPracticeId();

        Long setId = req.getSetId();
        practicePO.setSetId(setId);             //套卷id

        //拼接用时
        String timeUse = req.getTimeUse();
        String hour = timeUse.substring(0, 2);
        String minute = timeUse.substring(2, 4);
        String second = timeUse.substring(4, 6);
        practicePO.setTimeUse(hour + ":" + minute + ":" + second);

        //提交时间
        practicePO.setSubmitTime(DateUtils.parseStrToDate(req.getSubmitTime()));

        //完成状态
        practicePO.setCompleteStatus(CompleteStatusEnum.COMPLETE.getCode());
        practicePO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        practicePO.setCreatedBy(LoginUtil.getLoginId());
        practicePO.setCreatedTime(new Date());


        //计算正确率
        //用户作对的总数量
        Integer correctCount = practiceDetailDao.selectCorrectCount(practiceId);

        //获得套卷的题目总数
        List<PracticeSetDetailPO> practiceSetDetailPOS = practiceSetDetailDao.selectBySetId(setId);
        Integer totalCount = practiceSetDetailPOS.size();

        //计算正确率,保留4位向上取整
        BigDecimal correctRate = new BigDecimal(correctCount).divide(new BigDecimal(totalCount), 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100.00"));
        practicePO.setCorrectRate(correctRate);

        //查询
        PracticePO po = practiceDao.selectById(practiceId);

        //看看是不是重复练习
        if (Objects.isNull(po)) {
            practiceDao.insert(practicePO);
        } else {
            practicePO.setId(practiceId);
            practiceDao.update(practicePO);
        }

        //给套题热度+1
        practiceSetDao.updateHeat(setId);

        //补充剩余题目的记录

        //这里是用户作答的所有题目
        List<PracticeDetailPO> practiceDetailPOList = practiceDetailDao.selectByPracticeId(practiceId);

        //把没有作答的题目收集起来
        List<PracticeSetDetailPO> minusList = practiceSetDetailPOS.stream()
                .filter(item -> !practiceDetailPOList.stream()
                        .map(e -> e.getSubjectId())
                        .collect(Collectors.toList())
                        .contains(item.getSubjectId()))
                .collect(Collectors.toList());

        if (log.isInfoEnabled()) {
            log.info("题目差集{}", JSON.toJSONString(minusList));
        }

        //把差集插入进去,没做的题目我们也去插入一下
        if (CollectionUtils.isNotEmpty(minusList)) {
            minusList.forEach(e -> {
                PracticeDetailPO practiceDetailPO = new PracticeDetailPO();

                practiceDetailPO.setPracticeId(practiceId);
                practiceDetailPO.setSubjectType(e.getSubjectType());
                practiceDetailPO.setSubjectId(e.getSubjectId());
                practiceDetailPO.setAnswerStatus(0);
                practiceDetailPO.setAnswerContent("");
                practiceDetailPO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
                practiceDetailPO.setCreatedTime(new Date());
                practiceDetailPO.setCreatedBy(LoginUtil.getLoginId());

                //插入题目
                practiceDetailDao.insertSingle(practiceDetailPO);
            });
        }


        return true;
    }


    //提交题目
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean submitSubject(SubmitSubjectDetailReq req) {
        //获得该题的用时
        String timeUse = req.getTimeUse();

        //用户在一秒内提交的话,就是0来的,这时前端的控件
        if (timeUse.equals("0")) {
            timeUse = "000000";
        }

        //时间拆分
        String hour = timeUse.substring(0, 2);
        String minute = timeUse.substring(2, 4);
        String second = timeUse.substring(4, 6);

        //封装参数
        PracticePO practicePO = new PracticePO();
        practicePO.setId(req.getPracticeId());      //套卷id

        //拼接用时
        practicePO.setTimeUse(hour + ":" + minute + ":" + second);
        practicePO.setSubmitTime(new Date());       //提交时间

        //更新,套卷表的时间,我们没提交一个题,更新一次
        practiceDao.update(practicePO);

        //套卷详情表
        PracticeDetailPO practiceDetailPO = new PracticeDetailPO();
        practiceDetailPO.setPracticeId(req.getPracticeId());
        practiceDetailPO.setSubjectId(req.getSubjectId());
        practiceDetailPO.setSubjectType(req.getSubjectType());

        //题目答案
        String answerContent = "";

        //排序答案
        if (CollectionUtils.isNotEmpty(req.getAnswerContents())) {
            List<Integer> answerContents = req.getAnswerContents();

            //排序,这样有利于后面答案的校验
            Collections.sort(answerContents);
            answerContent = StringUtils.join(answerContents, ",");
        }

        //设置答案
        practiceDetailPO.setAnswerContent(answerContent);

        //题目信息
        SubjectDTO subjectDTO = new SubjectDTO();
        subjectDTO.setSubjectId(req.getSubjectId());
        subjectDTO.setSubjectType(req.getSubjectType());

        //获取正确答案，并判断答案是否正确
        SubjectDetailDTO subjectDetail = getSubjectDetail(subjectDTO);

        //正确的答案
        StringBuffer correctAnswer = new StringBuffer();

        //判断题的话,就直接就是正确还是错误即可
        if (req.getSubjectType().equals(SubjectInfoTypeEnum.JUDGE.getCode()))
        {
            Integer isCorrect = subjectDetail.getIsCorrect();
            correctAnswer.append(isCorrect);
        }
        else
        {
            //如果是其他的话就要拼接答案了
            subjectDetail.getOptionList().forEach(e -> {
                if (Objects.equals(e.getIsCorrect(), 1))        //如果是1是正确的奥,拼接答案进来
                {
                    correctAnswer.append(e.getOptionType()).append(",");
                }
            });

            //去掉末尾的逗号
            if (correctAnswer.length() > 0) {
                correctAnswer.deleteCharAt(correctAnswer.length() - 1);
            }
        }

        //比较内容和答案
        if (Objects.equals(correctAnswer.toString(), answerContent))
        {
            //答对了
            practiceDetailPO.setAnswerStatus(1);
        }
        else
        {
            //答错了
            practiceDetailPO.setAnswerStatus(0);
        }


        //把数据封装到套卷详情表
        practiceDetailPO.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        practiceDetailPO.setCreatedBy(LoginUtil.getLoginId());          //用户
        practiceDetailPO.setCreatedTime(new Date());

        //插入
        PracticeDetailPO existDetail = practiceDetailDao.selectDetail(req.getPracticeId(), req.getSubjectId(), LoginUtil.getLoginId());


        if (Objects.isNull(existDetail))
        {   //如果不是空的就是第一次写来的
            practiceDetailDao.insertSingle(practiceDetailPO);
        }
        else
        {
            //不是空的就更新来的,根据id更新
            practiceDetailPO.setId(existDetail.getId());
            practiceDetailDao.update(practiceDetailPO);
        }


        return true;
    }


    //查询题目详情,封装答案回来
    public SubjectDetailDTO getSubjectDetail(SubjectDTO dto) {
        //题目相信信息表
        SubjectDetailDTO subjectDetailDTO = new SubjectDetailDTO();

        SubjectPO subjectPO = subjectDao.selectById(dto.getSubjectId());

        //单选
        if (dto.getSubjectType() == SubjectInfoTypeEnum.RADID.getCode()) {
            List<SubjectOptionDTO> optionList = new LinkedList<>();

            //查询单选的选项
            List<SubjectRadioPO> radioSubjectPOS = subjectRadioDao.selectBySubjectId(subjectPO.getId());

            radioSubjectPOS.forEach(e -> {
                //封装数据
                SubjectOptionDTO subjectOptionDTO = new SubjectOptionDTO();
                subjectOptionDTO.setOptionContent(e.getOptionContent());
                subjectOptionDTO.setOptionType(e.getOptionType());
                subjectOptionDTO.setIsCorrect(e.getIsCorrect());

                optionList.add(subjectOptionDTO);
            });

            //设置答案
            subjectDetailDTO.setOptionList(optionList);
        }

        //多选
        if (dto.getSubjectType() == SubjectInfoTypeEnum.MULTIPLE.getCode()) {
            List<SubjectOptionDTO> optionList = new LinkedList<>();

            //查询多选的选项
            List<SubjectMultiplePO> multipleSubjectPOS = subjectMultipleDao.selectBySubjectId(subjectPO.getId());

            multipleSubjectPOS.forEach(e -> {
                //封装数据
                SubjectOptionDTO subjectOptionDTO = new SubjectOptionDTO();
                subjectOptionDTO.setOptionContent(e.getOptionContent());
                subjectOptionDTO.setOptionType(e.getOptionType());
                subjectOptionDTO.setIsCorrect(e.getIsCorrect());

                optionList.add(subjectOptionDTO);
            });

            //设置答案
            subjectDetailDTO.setOptionList(optionList);
        }

        //判断
        if (dto.getSubjectType() == SubjectInfoTypeEnum.JUDGE.getCode()) {
            SubjectJudgePO judgeSubjectPO = subjectJudgeDao.selectBySubjectId(subjectPO.getId());

            //我们这里就只用设置一下对不对即可
            subjectDetailDTO.setIsCorrect(judgeSubjectPO.getIsCorrect());
        }


        //题目解析
        subjectDetailDTO.setSubjectParse(subjectPO.getSubjectParse());

        //题目名称
        subjectDetailDTO.setSubjectName(subjectPO.getSubjectName());

        return subjectDetailDTO;
    }


    //获得用户所有题目的对错
    @Override
    public List<ScoreDetailVO> getScoreDetail(GetScoreDetailReq req) {
        //获得用户的练习id
        Long practiceId = req.getPracticeId();

        List<ScoreDetailVO> list = new LinkedList<>();

        //查询出来用户所有的作答状态
        List<PracticeDetailPO> practiceDetailPOList = practiceDetailDao.selectByPracticeId(practiceId);

        if (CollectionUtils.isEmpty(practiceDetailPOList)) {
            return Collections.emptyList();
        }

        practiceDetailPOList.forEach(po -> {
            //封装数据返回
            ScoreDetailVO scoreDetailVO = new ScoreDetailVO();
            scoreDetailVO.setSubjectId(po.getSubjectId());
            scoreDetailVO.setSubjectType(po.getSubjectType());
            scoreDetailVO.setIsCorrect(po.getAnswerStatus());       //题目作答状态对错

            list.add(scoreDetailVO);
        });

        //返回
        return list;
    }


    //查看题目的详细信息
    @Override
    public SubjectDetailVO getSubjectDetail(GetSubjectDetailReq req) {
        SubjectDetailVO subjectDetailVO = new SubjectDetailVO();

        Long subjectId = req.getSubjectId();                //题目id
        Integer subjectType = req.getSubjectType();         //题目类型

        SubjectDTO subjectDTO = new SubjectDTO();
        subjectDTO.setSubjectId(subjectId);
        subjectDTO.setSubjectType(subjectType);

        //返回题目的详细信息
        SubjectDetailDTO subjectDetail = getSubjectDetail(subjectDTO);

        //获得选项,如果是判断题的话这里的optionList就是空的
        List<SubjectOptionDTO> optionList = subjectDetail.getOptionList();

        //封装数据返回
        List<PracticeSubjectOptionVO> optionVOList = new LinkedList<>();

        List<Integer> correctAnswer = new LinkedList<>();           //正确答案

        if (CollectionUtils.isNotEmpty(optionList)) {
            optionList.forEach(option -> {
                //因为类型不同所以我们重新set一下
                PracticeSubjectOptionVO optionVO = new PracticeSubjectOptionVO();

                optionVO.setOptionType(option.getOptionType());
                optionVO.setOptionContent(option.getOptionContent());
                optionVO.setIsCorrect(option.getIsCorrect());

                optionVOList.add(optionVO);

                //这里就是你封装正确的答案
                if (option.getIsCorrect() == 1) {
                    correctAnswer.add(option.getOptionType());
                }
            });
        }


        //判断兼容来的,因为判断题只有iscurront这个字段的,但是我们也要展示出来的,所以我这里自己设置一下亲
        if (subjectType.equals(SubjectInfoTypeEnum.JUDGE.getCode())) {
            //看看是不是正确的
            Integer isCorrect = subjectDetail.getIsCorrect();

            PracticeSubjectOptionVO correctOption = new PracticeSubjectOptionVO();
            correctOption.setOptionType(1);
            correctOption.setOptionContent("正确");
            correctOption.setIsCorrect(isCorrect == 1 ? 1 : 0);

            PracticeSubjectOptionVO errorOptionVO = new PracticeSubjectOptionVO();
            errorOptionVO.setOptionType(2);
            errorOptionVO.setOptionContent("错误");
            errorOptionVO.setIsCorrect(isCorrect == 0 ? 1 : 0);

            optionVOList.add(correctOption);
            optionVOList.add(errorOptionVO);

            //封装判断的正确答案
            correctAnswer.add(subjectDetail.getIsCorrect());
        }

        subjectDetailVO.setOptionList(optionVOList);                            //选项
        subjectDetailVO.setSubjectParse(subjectDetail.getSubjectParse());       //解析
        subjectDetailVO.setSubjectName(subjectDetail.getSubjectName());
        subjectDetailVO.setCorrectAnswer(correctAnswer);                        //正确答案


        //自己的答题答案
        List<Integer> respondAnswer = new LinkedList<>();
        PracticeDetailPO practiceDetailPO = practiceDetailDao.selectAnswer(req.getPracticeId(), subjectId);

        //封装用户自己的答案
        String answerContent = practiceDetailPO.getAnswerContent();
        if (StringUtils.isNotBlank(answerContent)) {
            String[] split = answerContent.split(",");

            for (String s : split) {
                respondAnswer.add(Integer.valueOf(s));
            }
        }

        //设置自己的答案
        subjectDetailVO.setRespondAnswer(respondAnswer);

        //标签信息
        List<SubjectMappingPO> subjectMappingPOList = subjectMappingDao.getLabelIdsBySubjectId(subjectId);

        List<Long> labelIdList = new LinkedList<>();

        //收集标签id
        subjectMappingPOList.forEach(subjectMappingPO -> {
            labelIdList.add(subjectMappingPO.getLabelId());
        });


        //把标签名称全部查询出来
        List<String> labelNameList = subjectLabelDao.getLabelNameByIds(labelIdList);
        subjectDetailVO.setLabelNames(labelNameList);


        //返回
        return subjectDetailVO;
    }

    //获得评估报告
    @Override
    public ReportVO getReport(GetReportReq req) {
        ReportVO reportVO = new ReportVO();

        //练习id
        Long practiceId = req.getPracticeId();

        //查出来我们的套卷作答表
        PracticePO practicePO = practiceDao.selectById(practiceId);

        //套卷id
        Long setId = practicePO.getSetId();

        //查出来套卷
        PracticeSetPO practiceSetPO = practiceSetDao.selectById(setId);

        //设置名称
        reportVO.setTitle(practiceSetPO.getSetName());

        //获得用户所有题目的作答状态
        List<PracticeDetailPO> practiceDetailPOList = practiceDetailDao.selectByPracticeId(practiceId);

        if (CollectionUtils.isEmpty(practiceDetailPOList)) {
            return null;
        }

        //题目总数
        int totalCount = practiceDetailPOList.size();

        //把正确的题目收集起来
        List<PracticeDetailPO> correctPoList = practiceDetailPOList
                .stream()
                .filter(e -> Objects.equals(e.getAnswerStatus(), AnswerStatusEnum.CORRECT.getCode()))
                .collect(Collectors.toList());

        //3/20这样的设置会去
        reportVO.setCorrectSubject(correctPoList.size() + "/" + totalCount);

        //创建技能图谱
        List<ReportSkillVO> reportSkillVOS = new LinkedList<>();

        Map<Long, Integer> totalMap = getSubjectLabelMap(practiceDetailPOList);     //全部题目
        Map<Long, Integer> correctMap = getSubjectLabelMap(correctPoList);          //用户正确的题目

        //计算正确率
        totalMap.forEach((key, val) -> {
            ReportSkillVO skillVO = new ReportSkillVO();

            //查询标签名称
            SubjectLabelPO labelPO = subjectLabelDao.queryById(key);
            String labelName = labelPO.getLabelName();

            //获得用户作对的题目
            Integer correctCount = correctMap.get(key);

            if (Objects.isNull(correctCount)) {
                correctCount = 0;
            }

            //设置名称
            skillVO.setName(labelName);

            //正确率
            BigDecimal rate = BigDecimal.ZERO;

            //计算正确率
            if (!Objects.equals(val, 0)) {
                rate = new BigDecimal(correctCount.toString()).divide(new BigDecimal(val.toString()), 4,
                        BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100));
            }

            //正确率
            skillVO.setStar(rate);

            //添加这个标签的图谱
            reportSkillVOS.add(skillVO);

        });


        if (log.isInfoEnabled()) {
            log.info("获取到的正确率{}", JSON.toJSONString(reportSkillVOS));
        }

        //设置所有标签的技能图谱
        reportVO.setSkill(reportSkillVOS);

        return reportVO;
    }

    //获得题目的标签和标签出现的次数
    private Map<Long, Integer> getSubjectLabelMap(List<PracticeDetailPO> practiceDetailPOList) {
        if (CollectionUtils.isEmpty(practiceDetailPOList)) {
            return Collections.emptyMap();
        }

        Map<Long, Integer> map = new HashMap<>();

        practiceDetailPOList.forEach(detail -> {
            //题目id
            Long subjectId = detail.getSubjectId();

            //和题目有关的所有标签id
            List<SubjectMappingPO> labelIdPO = subjectMappingDao.getLabelIdsBySubjectId(subjectId);

            labelIdPO.forEach(po -> {
                Long labelId = po.getLabelId();

                if (Objects.isNull(map.get(labelId))) {
                    map.put(labelId, 1);

                    //下一次循环
                    return;
                }

                map.put(labelId, map.get(labelId) + 1);
            });
        });


        if (log.isInfoEnabled()) {
            log.info("获取到的题目对应的标签map{}", JSON.toJSONString(map));
        }

        return map;
    }

    //获得练习榜用户
    @Override
    public List<RankVO> getPracticeRankList() {
        List<RankVO> list = new LinkedList<>();

        //获得前5名用户
        List<PracticeRankPO> poList = practiceDetailDao.getPracticeCount();

        if (CollectionUtils.isEmpty(poList)) {
            return list;
        }

        //封装返回对象
        poList.forEach(e -> {
            RankVO rankVO = new RankVO();
            rankVO.setCount(e.getCount());                              //练习总数

            //获得用户信息
            UserInfo userInfo = userRpc.getUserInfo(e.getCreatedBy());
            rankVO.setName(userInfo.getNickName());
            rankVO.setAvatar(userInfo.getAvatar());

            //添加
            list.add(rankVO);
        });

        //返回集合
        return list;
    }


    //放弃答题
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean giveUp(Long practiceId, String loginId) {
        //删除用户的做大详情表
        practiceDetailDao.deleteByPracticeId(practiceId, loginId);

        int i=1/0;

        //删除用户的套卷表
        practiceDao.deleteById(practiceId, loginId);

        return true;
    }


}